package com.hotel.project.common.util;

import org.springframework.util.ConcurrentReferenceHashMap;

import java.beans.Introspector;
import java.io.Serializable;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.function.Function;
import java.util.regex.Pattern;

/**
 * 反射工具类
 *
 * @author: peng.ni
 * @date: 2022/04/13
 */
public class ReflectionsUtil {
    public interface ConvertFunction<T, RS> extends Function<T, RS>, Serializable {
    }

    private static final Pattern GET_PATTERN = Pattern.compile("get[A-Z].*");
    private static final Pattern IS_PATTERN = Pattern.compile("is[A-Z].*");

    private static ConcurrentReferenceHashMap<String, String> cache = new ConcurrentReferenceHashMap<String, String>(64, ConcurrentReferenceHashMap.ReferenceType.SOFT);

    /**
     * 根据传入的getter方法或者is方法，获取对应的字段名
     *
     * @param convertFunction 类的getter方法或者is方法
     * @param <T>
     * @param <RS>
     * @return
     * @throws Exception
     */
    public static <T, RS> String convert(ConvertFunction<T, RS> convertFunction) {
        Class<? extends ConvertFunction> convertFunctionClass = convertFunction.getClass();
        String canonicalName = convertFunctionClass.getCanonicalName();
        if (cache.containsKey(canonicalName)) {
            return cache.get(canonicalName);
        } else {
            long start = System.currentTimeMillis();
            Class<? extends Function> aClass = convertFunction.getClass();

            try {
                Method writeReplace = aClass.getDeclaredMethod("writeReplace");
                writeReplace.setAccessible(true);
                SerializedLambda serializedLambda = (SerializedLambda) writeReplace.invoke(convertFunction);
                // 获取方法名
                String implMethodName = serializedLambda.getImplMethodName();
                // 切分获取字段名
                if (GET_PATTERN.matcher(implMethodName).matches()) {
                    implMethodName = implMethodName.substring(3);
                } else if (IS_PATTERN.matcher(implMethodName).matches()) {
                    implMethodName = implMethodName.substring(2);
                }
                String filedName = Introspector.decapitalize(implMethodName);
                cache.put(canonicalName, filedName);
                return filedName;
            } catch (Exception e) {
                throw new RuntimeException("反射获取类字段出错");
            }
        }
    }
}
